﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using IronPython.Hosting;
using MetaNote.Controls;
using MetaNote.Model;
using Microsoft.Scripting.Hosting;
using DocumentDictionary = System.Collections.Generic.Dictionary<System.Guid, MetaNote.Model.Document>;
using MacroDictionary = System.Collections.Generic.Dictionary<System.Guid, MetaNote.Model.Macro>;
using ToolStripDictionary = System.Collections.Generic.Dictionary<System.Guid, System.Windows.Forms.ToolStripItem>;
namespace MetaNote
{
    public partial class MainForm : Form
    {
        private ScriptEngine m_engine = null;
        private ScriptScope m_scope = null;
        private DocumentDictionary m_documents = new DocumentDictionary();
        private MacroDictionary m_macros = new MacroDictionary();
        private ToolStripDictionary m_toolStripItemsWithMacros = new ToolStripDictionary();
        private HashSet<string> m_recentFiles = new HashSet<string>();
        private Document m_currentDocument = null;
        private int m_seq = 0;

        private Document CurrentDocument
        {
            get
            {
                return m_currentDocument;
            }
            set
            {
                if (m_currentDocument != value)
                {
                    m_currentDocument = value;
                    SetTitles(this.CurrentDocument);
                }
            }
        }

        private int GetNextSeq()
        {
            m_seq++;
            return m_seq;
        } //This was one of those seemingly simple methods in which guys like joe duffy find umpteen threading problems.

        public MainForm()
        {
            InitializeComponent();
            //TODO: If shutdown was unexpected last time, ask user which macros to load/enable
            //      (and/or, archive old macros into a backup location, just in case.)
            m_macros = Common.LoadMacros();
            m_recentFiles = Common.LoadRecentFiles();
            
            AddMenuItems(recentFilesToolStripMenuItem, m_recentFiles.Reverse());

            //TODO: Common.LoadMenu(mainMenuStrip, m_macros, MacroButton_Click);
            Common.LoadMenu(mainToolStrip, m_macros, MacroButton_Click);
            AddEditHandlers(this.mainMenuStrip);            
        }

        private void AddMenuItems(ToolStripMenuItem recentFilesToolStripMenuItem, IEnumerable<string> m_recentFiles)
        {
            foreach (string s in m_recentFiles)
            {
                recentFilesToolStripMenuItem.DropDownItems.Add(s, null, recentFile_Click);
            }
        }

        private void recentFile_Click(object sender, EventArgs e)
        {
            Document doc = this.CreateDocumentTab(GetEmptyDocument());
            if (doc.Open((sender as ToolStripItem).Text))
            {
                this.CurrentDocument = doc;                
            }
        }
        
        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            NewDocument();            
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenDocument();
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveCurrentDocument();
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveCurrentDocumentAs();
        }
        
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ExitApplication();
        }

        private void undoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CurrentDocument.CurrentTextBox.Undo();
        }
        
        private void redoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CurrentDocument.CurrentTextBox.Redo();
        }

        private void cutToolStripMenuItem_Click(object sender, EventArgs e)
        {
                CurrentDocument.CurrentTextBox.Cut();
        }

        private void copyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CurrentDocument.CurrentTextBox.Copy();
        }

        private void pasteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CurrentDocument.CurrentTextBox.Paste();
        }
        
        private void selectAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CurrentDocument.CurrentTextBox.SelectAll();
        }

        private void startRecordingToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: start recording
            ShowTodo("start recording"); 
        }

        private void stopRecordingToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: stop recording
            ShowTodo("stop recording"); 
        }

        private void replayRecordedMacroToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: replay current macro
            ShowTodo("replay current macro"); 
        }

        private void editRecordedMacroToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: edit macro
            ShowTodo("edit macro"); 
        }

        private void addinsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: show addins dialog
            ShowTodo("show addins dialog"); 
        }

        private void externalToolsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: show external tools 
            ShowTodo("show external tools (e.g. html viewer, compiler, ftp client, zip, image edit"); 
        }

        private void optionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: show options dialog
            ShowTodo("show options dialog"); 
        }

        private void makeASuggestionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: 'make a suggestion' to somewhere
            ShowTodo("'make a suggestion' to somewhere");
            //Common.StartDefaultWebBrowser("http://URL goes here");
        }

        private void submitABugReportToolStripMenuItem_Click(object sender, EventArgs e)
        {
            GotoNewBugReport();
        }

        private void instructionalWikionlineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            GotoWiki();
        }
        
        private void checkForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: Check for update, 
            //TODO: (later) allow autoupdate if update found
            ShowTodo("Check for update, allow autoupdate if one found");
            //Common.StartDefaultWebBrowser("http://URL");
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: show 'about' screen
            ShowTodo("show 'about' screen"); 
        }

        private void addMacroToolStripMenuItem_Click(object sender, EventArgs e)
        {
            int sequence = 0;
            
            var hitItem = FindItemAt(sender as ToolStripMenuItem);
            if (hitItem != null)
            {
                //Find sequence of hit item so we can add button just before/after
                sequence = FindIndex(mainToolStrip, hitItem);
            }
            AddButton(sequence, hitItem.Name, null);
        }
        
        private int FindIndex(ToolStrip mainToolStrip, ToolStripItem hitItem)
        {
            int sequence = 0;
            foreach (var item in mainToolStrip.Items)
            {
                if (item == hitItem) break;
                sequence++;
            }

            return sequence;
        }

        private void editMacroToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var hitItem = FindItemAt(sender as ToolStripMenuItem);
            if (hitItem != null && hitItem is ToolStripItem && (hitItem as ToolStripItem).Tag != null)
            {
                EditToolbarMacro(hitItem as ToolStripItem);
            }                        
        }

        private ToolStripItem FindItemAt(ToolStripMenuItem sender)
        {
            var parentMenu = (sender.GetCurrentParent() as ContextMenuStrip);
            return FindItemAt(parentMenu);            
        }

        private ToolStripItem FindItemAt(ContextMenuStrip parentMenu)
        {
            //'Finding' the item we're trying to edit is ofcourse bloody hard!
            var pointOnParent = parentMenu.PointToClient(new System.Drawing.Point(0, 0));

            //7 is a magic number... probably to do with width of form's chrome? or tabstrip padding
            var hitItem = mainToolStrip.GetItemAt(new Point((pointOnParent.X * -1) - this.Location.X - 7, 2));
            return hitItem;
        }

        private void EditToolbarMacro(ToolStripItem toolStripItem)
        {
            var codeKey = (Guid)toolStripItem.Tag;
            EditToolbarMacro(codeKey, toolStripItem);
        }

        private void EditToolbarMacro(Guid codeKey, ToolStripItem controlbeingEdited)
        {
            if (tabControl.TabPages.ContainsKey(codeKey.ToString()))
            {
                //show the relevant tab (it was already in edit mode)
                tabControl.SelectedTab = tabControl.TabPages[codeKey.ToString()];
            }
            else
            {
                EditToolBarButton(codeKey, controlbeingEdited);
                //(it's not currently visible).
            }
        }

        private void EditToolBarButton(Guid codeKey, ToolStripItem controlbeingEdited)
        {
            var macro = m_macros[codeKey];
            Document d = new Document(codeKey, 0, macro.Title);
            d.Content = macro.SourceCode;
            CreateDocumentTab(d);

            AddCodeControls(this.CurrentDocument, this.CurrentDocument.CurrentTextBox, macro.Title, controlbeingEdited);
            
            tabControl.SelectedTab = tabControl.TabPages[d.Key.ToString()];
        }

        private void recentMacrosHereToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: show recent macro in editor.
            ShowTodo("show recent macro in editor.");
        }

        private void addButtonToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Control parent = (sender as Control);
            string parentName = null;
            if (parent != null) parentName = parent.Name;
            AddButton(mainToolStrip.Items.Count -1, parentName, null);
        }

        private void AddButton(int sequence, string parentName, string ownerName)
        {
            Guid newButtonGuid = Guid.NewGuid();
            string startingTitle = "(new)";
            ToolStripButton controlBeingEdited = AddButton(startingTitle, null, newButtonGuid, sequence, parentName, ownerName);
            CreateDocumentTab(GetEmptyDocument(newButtonGuid));
            //just for now -- a default example piece of code.
            this.CurrentDocument.CurrentTextBox.Text = "txt.SelectedText = txt.SelectedText.upper()";
            
            AddCodeControls(this.CurrentDocument, this.CurrentDocument.CurrentTextBox, startingTitle, controlBeingEdited);
            
        }

        private void AddCodeControls(Document codeDocument, NiceTextBox txt1, string title, ToolStripItem controlBeingEdited)
        {
            var macros = (from p in m_macros.Values select p).ToList();

            var codeEditPanel = new CodeEditPanel(txt1, title, macros, controlBeingEdited);
            codeEditPanel.Dock = DockStyle.Bottom;
            codeEditPanel.SaveCode += new CodeEditPanel.CodeSaveHandler(this.SaveCode);
            codeEditPanel.MacroSelectionChanged += new CodeEditPanel.MacroSelectionChangedHandler(this.MacroSelectionChanged);
            tabControl.TabPages[codeDocument.Key.ToString()].Controls.Add(codeEditPanel);
        }

        private Document GetEmptyDocument(Guid guid)
        {
            return new Document(guid, this.GetNextSeq(), null);
        }

        private Document GetEmptyDocument()
        {
            return GetEmptyDocument(Guid.NewGuid());
        }

        private Document CreateDocumentTab(Document d)
        {
            m_documents.Add(d.Key, d);
            tabControl.TabPages.Add(d.Key.ToString(), d.TabName);

            var txt1 = new NiceTextBox();//...TextBox or something better, eh!?
            txt1.Font = Common.Settings.Font;
            //txt1.Multiline = true;
            txt1.ShowVRuler = false;
            txt1.AutoScroll = true;
            txt1.ShowLineNumbers = false;
            //txt1.ScrollBars = ScrollBars.Vertical;
            txt1.Dock = DockStyle.Fill;
            txt1.Tag = d.Key;
            txt1.Text = d.Content;
            tabControl.TabPages[d.Key.ToString()].Controls.Add(txt1);
            tabControl.SelectedTab = tabControl.TabPages[d.Key.ToString()];
            txt1.TextChanged += new System.EventHandler(this.CurrentTextBox_TextChanged);
            SetCurrentDocument(txt1);
            return d;
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            m_engine = Python.CreateEngine();
            m_scope = m_engine.CreateScope();
            
            foreach (var key in m_macros.Keys)
            {
                var macro = m_macros[key];
                if (!macro.IsCompiled && macro.SourceCode != null)
                {
                    try
                    {
                        macro.Compile(m_engine);
                    }
                    catch (Exception ex)
                    {
                        //TODO: Different and better exception handling ;-)
                        MessageBox.Show("A problem while compiling a macro\r\n\r\n" 
                               + ex.ToString());
                        continue;
                    }
                }
                
            }
            
            //TODO: Populate 'windows' menu
            //TODO: If (commandline arg specifies a file) {load it;  }

            CreateDocumentTab(GetEmptyDocument());
        }

        private void AddEditHandlers(MenuStrip menuStrip)
        {
            AddEditHandlers(menuStrip.Items);
        }

        private void AddEditHandlers(ToolStripItemCollection toolStripItemCollection)
        {
            foreach (var item in toolStripItemCollection)
            {
                AddEditHandlers(item as ToolStripMenuItem);
            }
        }

        private void AddEditHandlers(ToolStripMenuItem toolStripMenuItem)
        {
            if (toolStripMenuItem == null) return;

            AddEditHandler(toolStripMenuItem);

            if (toolStripMenuItem.DropDownItems == null) return;

            foreach (var item in toolStripMenuItem.DropDownItems)
            {
                AddEditHandlers(item as ToolStripMenuItem);
            }
        }

        private void AddEditHandler(ToolStripMenuItem toolStripMenuItem)
        {
            toolStripMenuItem.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ToolStripMenuItem_MouseDown);
        }
        
        private void CurrentTextBox_TextChanged(object sender, EventArgs e)
        {
            this.CurrentDocument.IsDirty = true;

            SetTitles(this.CurrentDocument);
        }

        private void SetTitles(Document currentDocument)
        {
            if (currentDocument == null)
            {
                this.Text = "MetaNote";
                return;
            }
            this.Text = "MetaNote - " + currentDocument.TabName;

            if (tabControl.TabPages[currentDocument.Key.ToString()].Text != currentDocument.TabName)
                tabControl.TabPages[currentDocument.Key.ToString()].Text = currentDocument.TabName;
        }

        private void MacroSelectionChanged(object sender, MacroSelectionChangedArgs e)
        {
            if (e.Macro != null)
            {
                //TODO: Handle change of selected macro under edit.
            }
        }

        private void SaveCode(object sender, CodeSaveArgs e)
        {
            Debug.Assert(e.TextBox.Tag != null 
                        && !string.IsNullOrEmpty(e.TextBox.Tag.ToString()),
                        "The tag was set when the textbox was created.");
            var macro = e.Macro;

            //the textbox tag is the document key.
            Guid documentKey = (Guid)e.TextBox.Tag;

            //string code = e.TextBox.Text;
            
            //var macro = new Macro();
            //macro.SourceCode = code;
            //macro.Key = codeKey;
            //macro.Title = e.Name;
            ////Add macro to list
            
            m_macros.AddReplace(macro.Key, macro as Macro);

            try
            {
                macro.Compile(m_engine);
            }
            catch (Exception ex)
            {
                //TODO: Different and better exception handling
                MessageBox.Show("A problem while compiling your macro\r\n\r\n" + ex.ToString());
                return;
            }
            
            UpdateButton(e.Name, e.Image, macro.Key, e.Button, documentKey);
        }

        private void UpdateButton(string name, Image image, Guid macroKey, ToolStripItem newButton, Guid documentKey)
        {
            if (newButton == null)
            {
                throw new ArgumentException("Unknown button"); 
            }
            ToolStripItem parentItem = newButton.OwnerItem ?? newButton;
            //if (parentItem != null)
            //{
            //    m_macros[buttonKey].ParentName = parentItem.GetCurrentParent().Name;
            //    m_macros[buttonKey].OwnerName = parentItem.Name;
            //}
            //else
            //{
            //}

            ToolStripMenuItem t = (parentItem as ToolStripMenuItem);
            //int seq = 0;
            //if (t != null)
            //{
            //    seq = t.DropDownItems.Count - 1;
            //}
            //m_macros[buttonKey].Sequence = seq;
            m_documents[documentKey].Title = name;
            
            //tabControl.TabPages[codeKey.ToString()].Text = name;
            m_documents[documentKey].IsDirty = false;
            newButton.Image = image;
            newButton.Text = name;
            newButton.Tag = macroKey;
            SetTitles(this.CurrentDocument);
            HighlightButton(newButton);
        }

        private ToolStripButton AddButton(string name, Image image, Guid codeKey, int sequence, string parentName, string ownerName)
        {
            //Add button to tool bar
            var newButton = new ToolStripButton(name, image, MacroButton_Click, codeKey.ToString());
            newButton.Tag = codeKey;
            m_toolStripItemsWithMacros.Add(codeKey, newButton);

            //insert new buttons at start (not end)

            if (string.IsNullOrEmpty(parentName))
            {
                mainToolStrip.Items.Insert(sequence, newButton);
                return newButton;
            }

            Control[] potentialParents = this.Controls.Find(parentName, true);

            if (potentialParents != null && potentialParents.Length == 1)
            {
                ToolStrip parentToolStrip = (potentialParents[0] as ToolStrip);
                if (parentToolStrip != null)
                {
                    ToolStripMenuItem targetItem = (parentToolStrip.Items[ownerName] as ToolStripMenuItem);
                    if (targetItem != null)
                    {
                        targetItem.DropDownItems.Insert(sequence, newButton);
                    }
                    else
                    {
                        parentToolStrip.Items.Insert(sequence, newButton);
                    }
                    return newButton;
                }
            }

            mainToolStrip.Items.Insert(sequence, newButton);
            return newButton;
            //TODO: Have some kind of sequence for how toolbar positions are handled.
        }

        private void MacroButton_Click(object sender, EventArgs e)
        {
            Guid codeKey = (Guid)((sender as ToolStripItem).Tag);

            if (codeKey.Equals(new Guid()))
            {
                //TODO: internal macro... route to correct handler
                return;
            }


            if (!m_macros.ContainsKey(codeKey))
            {
                //Edit...
                //
                EditToolbarMacro(codeKey, (sender as ToolStripItem));
                
                return;
            }
            var macro = m_macros[codeKey];
            if (!macro.IsCompiled)
            {
                try
                {
                    macro.Compile(m_engine);
                }
                catch (Exception ex)
                {
                    //TODO: Different and better exception handling
                    MessageBox.Show("A problem while compiling a macro\r\n\r\n" + ex.ToString());
                    return;
                }
            }
            try
            {
                macro.CompiledScript.Execute(m_scope);
            }
            catch (Exception ex)
            {
                //TODO: Different and better exception handling
                MessageBox.Show("An unhandled exception occurred while executing this macro\r\n\r\n" + ex.ToString());
            }
        }

        private void tabControl_Selected(object sender, TabControlEventArgs e)
        {
            if (tabControl.TabPages.Count == 0) return;

            NiceTextBox txt1 = tabControl.SelectedTab.Controls[0] as NiceTextBox;
            if (txt1 != null)
            {
                txt1.Select();
                this.SetCurrentDocument(txt1);
            }

            if (tabControl.SelectedTab.Controls.Count == 1)
            {
                HighlightButton(null);
                return;
            }

            CodeEditPanel cep1 = tabControl.SelectedTab.Controls[1] as CodeEditPanel;
            if (cep1 != null)
            {                
                this.HighlightButton(cep1.ControlBeingEdited);
            } 
        }

        
        private void SetCurrentDocument(NiceTextBox txt1)
        {
            if (txt1 != null)
            {
                m_scope.SetVariable("txt", txt1);
                Guid key = (Guid)txt1.Tag;
                this.CurrentDocument = this.m_documents[key];
                this.CurrentDocument.CurrentTextBox = txt1;
                //HighlightButton(key);
            }            
        }

        private void HighlightButton(ToolStripItem toolStripItem)
        {
            if (toolStripItem == null)
            {
                this.HighlightMacro(Guid.NewGuid());
                return;
            }
            this.HighlightMacro((Guid)toolStripItem.Tag);
            toolStripItem.BackColor = System.Drawing.Color.White;
        }

        private void HighlightMacro(Guid key)
        {
            //var set = false;
            foreach (var item in mainToolStrip.Items)
            {
                if (item is ToolStripButton)
                {
                    if ((item as ToolStripButton).Tag.ToString() == key.ToString())
                    {
                        //TODO: Be more stylish about this!
                        (item as ToolStripButton).BackColor = System.Drawing.Color.WhiteSmoke;
                    }
                    else
                    {
                        (item as ToolStripButton).BackColor = System.Drawing.SystemColors.Control;
                    }
                }
            }
        }

        private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            Common.SaveMacros(m_macros);
            Common.SaveSettings();
            Common.SaveRecentFiles(m_recentFiles);
            Common.SaveMenu(mainMenuStrip);
            Common.SaveMenu(mainToolStrip);            
        }

        private void rearrangeButtonsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowTodo("Allow rearrange of buttons"); 
            //Maybe this could be done by just listing the buttons in a text editor window.
            //and the user can rearrange them manually (or with a macro) then close that window, done.
            //TODO: Allow drag/drop to reorder buttons
            //TODO: save index with each button
            //TODO: when adding buttons, use their 'index'
        }

        private void ShowTodo(string p)
        {
            Common.ShowTodo(p);
        }
       
        #region "selected tab trick - http://dotnetrix.co.uk/tabcontrol.htm"

        [Flags()]
        private enum TCHITTESTFLAGS
        {
            TCHT_NOWHERE = 1,
            TCHT_ONITEMICON = 2,
            TCHT_ONITEMLABEL = 4,
            TCHT_ONITEM = TCHT_ONITEMICON | TCHT_ONITEMLABEL
        }

        private const int TCM_HITTEST = 0x130D;

        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        private struct TCHITTESTINFO
        {
            public Point pt;
            public TCHITTESTFLAGS flags;
            public TCHITTESTINFO(int x, int y)
            {
                pt = new Point(x, y);
                flags = TCHITTESTFLAGS.TCHT_ONITEM;
            }
        }

#endregion

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern int SendMessage(IntPtr hwnd, int msg, IntPtr wParam, ref TCHITTESTINFO lParam);
        
        private void tabControl_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Middle)
            {
                TCHITTESTINFO HTI = new TCHITTESTINFO(e.X, e.Y);
                TabPage hotTab = tabControl.TabPages[SendMessage(tabControl.Handle, TCM_HITTEST, IntPtr.Zero, ref HTI)];
                if (ConfirmDocumentClosing(hotTab)) {
                    CloseDocument(hotTab);
                }
                SetTitles(this.CurrentDocument);
            }            
        }

        private void CloseDocument(TabPage tabPage)
        {
            var documentKey = new Guid("{" + tabPage.Name + "}");

            if (this.CurrentDocument.Key == documentKey)
            {
                this.CurrentDocument = null;
            }

            m_documents.Remove(documentKey);
            if (mainToolStrip.Items.ContainsKey(documentKey.ToString()) && !m_macros.ContainsKey(documentKey))
            {
                //TODO: fix this, no longer relevant
                //It's a macro that hasn't been saved yet.
                mainToolStrip.Items.RemoveByKey(documentKey.ToString());
            }
            else
            {
                //stop hightlighting the current macro
                HighlightMacro(new Guid());
            }
            tabControl.TabPages.Remove(tabPage);
        }

        /// <summary>
        /// Remove the document from the list -- *if* the user confirms it.
        /// </summary>
        /// <param name="tabPage">The tab page.</param>
        private bool ConfirmDocumentClosing(TabPage tabPage)
        {
            var documentKey = new Guid("{" + tabPage.Name + "}");

            return ConfirmDocumentClosing(documentKey); 
        }

        private bool ConfirmDocumentClosing(Guid documentKey)
        {
            if (!m_documents[documentKey].IsDirty) return true;

            var result = MessageBox.Show("Save your work " + 
                                    m_documents[documentKey].TabName + " ?", 
                                    "MetaNote - confirm document closing",
                                    MessageBoxButtons.YesNoCancel,
                                    MessageBoxIcon.Information);
            bool okayToClose = false;
            if (result == DialogResult.Yes)
            {
                okayToClose = m_documents[documentKey].Save(this);
            }
            else if (result == DialogResult.No)
            {
                okayToClose = true;
            }

            return okayToClose;
        }

        private void findToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var findReplace = new FindReplace(CurrentDocument);
            findReplace.ShowDialog(this);
        }

        private void fontsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetMainFont();
        }

        private void inlineFindToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: Find Inline ftw!
            ShowTodo("Find Inline");
        }

        private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CurrentDocument.CurrentTextBox.Delete(); 
        }

        private void toolbarContextMenu_Opened(object sender, EventArgs e)
        {        
            var hitItem = FindItemAt(sender as ContextMenuStrip);
            var isMacroButton = !(hitItem.Tag == null);
            editMacroToolStripMenuItem.Enabled = isMacroButton;
        }

        private void deleteToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            var hitItem = FindItemAt(sender as ToolStripMenuItem);
            if (hitItem != null && hitItem is ToolStripItem && (hitItem as ToolStripItem).Tag != null)
            {
                //DeleteMacro(hitItem as ToolStripItem);
                DeleteButton(hitItem as ToolStripItem);
            }
        }

        private void DeleteButton(ToolStripItem toolstripItem)
        {
            mainToolStrip.Items.Remove(toolstripItem);
            //TODO: if macro is no longer used by any button...
            // perhaps remove it!?
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            var unsaveddocuments = new DocumentDictionary();

            foreach (var document in m_documents.Values)
            {
                if (document.ToString().EndsWith(" *"))
                {
                    unsaveddocuments.Add(document.Key, document);
                }
            }

            if (unsaveddocuments.Count > 0)
            {
                var result = (new SaveWhichDocumentsForm(unsaveddocuments)).ShowDialog(this);
                if (result == DialogResult.Cancel)
                {
                    //update tab names
                    UpdateTabNames();

                    e.Cancel = true;
                }
            }

            m_recentFiles = GetFileNames(m_documents, m_recentFiles);            
        }

        private void UpdateTabNames()
        {
            foreach (Document item in m_documents.Values)
            {
                tabControl.TabPages[item.Key.ToString()].Text = item.TabName;
            }
            
            SetTitles(this.CurrentDocument);
        }

        private HashSet<string> GetFileNames(DocumentDictionary m_documents, HashSet<string> filenames)
        {
            foreach (var d in m_documents.Values)
            {
                if (d.FileName != null && !filenames.Contains(d.FileName))
                {
                    filenames.Add(d.FileName);
                }
            }

            return filenames;
        }

        private void findNextToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: Find next item
            ShowTodo("Find next item...");
        }

        private void replaceToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: Replace
            ShowTodo("Replace...");
        }

        private void gotoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: Goto (e.g. goto line number?)
            ShowTodo("Goto (e.g. line number)");
        }

        private void editToolStripMenuItem_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Middle)
            {
                Debug.WriteLine(sender.GetType().ToString());
            }
        }

        private void ToolStripMenuItem_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                if (sender is ToolStripDropDownItem && (sender as ToolStripDropDownItem).DropDownItems.Count > 0)
                {
                    AddMenuMacro(sender as ToolStripDropDownItem);
                }
                else
                {
                    EditMenuMacro(sender as ToolStripMenuItem);
                }
            }
            else if (e.Button == MouseButtons.Middle)
            {
                DeleteMenuMacro(sender as ToolStripMenuItem);
            }
        }

        private void DeleteMenuMacro(ToolStripMenuItem toolStripMenuItem)
        {
            //TODO: Delete menu macro... 
            if (toolStripMenuItem.Tag != null)
            {
                var guid = new Guid("{" + toolStripMenuItem.Tag.ToString() + "}");
                DeleteMenuMacro(guid);
            }
            else
            {
                MessageBox.Show("You can't delete this button (it's not a user macro)");
            }
        }

        private void DeleteMenuMacro(Guid guid)
        {
            //TODO: Delete menu macro
            ShowTodo("Delete Menu Macro");
        }

        /// <summary>
        /// Adds the menu macro.
        /// </summary>
        /// <param name="toolStripDropDownItem">The tool strip drop down item.</param>
        private void AddMenuMacro(ToolStripDropDownItem toolStripDropDownItem)
        {
            //Add a new menu item under this top level menu...
            CreateDocumentTab(GetEmptyDocument());
            //just for now -- a default example piece of code.
            this.CurrentDocument.CurrentTextBox.Text = "txt.SelectedText = txt.SelectedText.upper()";
            string startingTitle = "(new)";
            int seq = toolStripDropDownItem.DropDownItems.Count;
            ToolStripItem toolStripItemBeingEdited = AddMenuItem(toolStripDropDownItem, startingTitle, this.CurrentDocument.Key, seq);

            AddCodeControls(this.CurrentDocument, this.CurrentDocument.CurrentTextBox, startingTitle, toolStripItemBeingEdited);
            
           
        }

        private ToolStripItem AddMenuItem(ToolStripDropDownItem toolStripDropDownItem, string startingTitle, Guid guid, int seq)
        {
            ToolStripItem toolStripItem = new ToolStripButton(startingTitle, null, MacroButton_Click, guid.ToString());
            toolStripItem.Tag = guid;
            toolStripDropDownItem.DropDownItems.Insert(seq, toolStripItem);
            m_toolStripItemsWithMacros.Add(guid, toolStripItem);
            return toolStripItem;
        }

        private void EditMenuMacro(ToolStripMenuItem toolStripMenuItem)
        {
            if (toolStripMenuItem.Tag != null)
            {
                var guid = new Guid("{" + toolStripMenuItem.Tag.ToString() + "}");
                EditMenuMacro(guid);
            }
            else
            {
                MessageBox.Show("You can't edit this button (it's not a user macro)");
            }
        }

        private void EditMenuMacro(Guid guid)
        {
            //throw new NotImplementedException();
        }

        private void newDocumentToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CreateDocumentTab(GetEmptyDocument()); 
        }

        private void closeDocumentToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: Close document (from context menu)
            ShowTodo("Close document (from context menu)");
        }

        private void closeOtherDocumentsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //TODO: Close other documents (from context menu)
            ShowTodo("Close other documents (from context menu)");
        }

        private void NewDocument()
        {
            CreateDocumentTab(GetEmptyDocument());
        }

        private void OpenDocument()
        {
            Document doc = this.CreateDocumentTab(GetEmptyDocument());

            if (doc.Open(this))
            {
                this.CurrentDocument = doc;
            }
        }
        
        private void SaveCurrentDocument()
        {
            this.CurrentDocument.Save(this);
            SetTitles(this.CurrentDocument);
        }

        private void SaveCurrentDocumentAs()
        {
            this.CurrentDocument.Saveas(this);
            SetTitles(this.CurrentDocument);
        }

        private void ExitApplication()
        {
            this.Close();
        }
        

        private static void GotoNewBugReport()
        {
            Common.StartDefaultWebBrowser("http://code.google.com/p/metanote/issues/list");
        }

        private static void GotoWiki()
        {
            Common.StartDefaultWebBrowser("http://code.google.com/p/metanote/w/list");
        }

        private void SetMainFont()
        {
            var fontDialog = new FontDialog();
            fontDialog.Font = Common.Settings.Font;
            var result = fontDialog.ShowDialog();
            if (result == DialogResult.OK)
            {
                Common.Settings.Font = fontDialog.Font;
                foreach (var document in m_documents.Values)
                {
                    document.CurrentTextBox.Font = fontDialog.Font;
                }
            }
        }
    }

    //TODO: Ctrl-F4 to close a document
    //TODO: Macros that load into the menu system don't load up into the menu system (they add in as buttons)
    //TODO: Hover on a tab... see the file name with full path.
    //TODO: Delete a macro, should be less dangerous. (Middle click is very easy)
    // it could be that there's an undo, 
    // and/or that macros are backed up somewhere.
    // and/or that the user is asked to confirm.
    //if a macro is unsaved, and not dirty, then deleting should continue to be as easy.

    //TODO: Features such as editing the menu are not discoverable, because no-one *ever* right clicks or middle clicks a menu.
    //TODO: Keep 'Windows' menu up to date
    //TODO: When document opened, update Windows menu
    //TODO: When document closed, update Windows menu
    //TODO: When document saved, update Windows menu name
    //TODO: When macro saved, update Windows menu name
    //GOAL: All menu items are actually macros.
    //File
    // New
    // Open
    // Save
    // Save as
    // Recent Files >
    //  (Open file)
    // Exit
    //Edit
    // Undo
    // Redo
    // Cut
    // Copy
    // Paste
    // Select all
    // Find
    // Find Next
    // Replace
    // Find inline
    // Goto
    // Select all
    //Meta 
    // Macro Recording >
    //  Start recording
    //  Stop recording
    //  Replay macro
    //  Edit macro
    //  Recent Macros  
    // Options
    // Font
    //Window
    // (populate list of windows)
    // (activate window)
    //Help
    // make a suggestion
    // submit a bug report
    // instructional wiki
    // check for updates
    // about

    //TODO: determine (at least a first guess of) the 'type' of files being saved...
    // e.g. -- if it's a macro, then go with .py.
    // if it's anything else go with whatever the last extension they saved was.
    // (and if they've never saved anything, assume .txt)


}